/*
 * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
 *
 * This software is distributable under the BSD license. See the terms of the
 * BSD license in the documentation provided with this software.
 */
package org.apache.geode.management.internal.cli.shell.jline;

import org.springframework.shell.support.util.OsUtils;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 *  A buffer that can contain ANSI text.
 *
 *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
 */
public class ANSIBuffer {
    private boolean ansiEnabled = true;
    private final StringBuffer ansiBuffer = new StringBuffer();
    private final StringBuffer plainBuffer = new StringBuffer();

    public ANSIBuffer() {
    }

    public ANSIBuffer(final String str) {
        append(str);
    }

    public void setAnsiEnabled(final boolean ansi) {
        this.ansiEnabled = ansi;
    }

    public boolean getAnsiEnabled() {
        return this.ansiEnabled;
    }

    public String getAnsiBuffer() {
        return ansiBuffer.toString();
    }

    public String getPlainBuffer() {
        return plainBuffer.toString();
    }

    public String toString(final boolean ansi) {
        return ansi ? getAnsiBuffer() : getPlainBuffer();
    }

    public String toString() {
        return toString(ansiEnabled);
    }

    public ANSIBuffer append(final String str) {
        ansiBuffer.append(str);
        plainBuffer.append(str);

        return this;
    }

    public ANSIBuffer attrib(final String str, final int code) {
        ansiBuffer.append(ANSICodes.attrib(code)).append(str)
                  .append(ANSICodes.attrib(ANSICodes.OFF));
        plainBuffer.append(str);

        return this;
    }

    public ANSIBuffer red(final String str) {
        return attrib(str, ANSICodes.FG_RED);
    }

    public ANSIBuffer blue(final String str) {
        return attrib(str, ANSICodes.FG_BLUE);
    }

    public ANSIBuffer green(final String str) {
        return attrib(str, ANSICodes.FG_GREEN);
    }

    public ANSIBuffer black(final String str) {
        return attrib(str, ANSICodes.FG_BLACK);
    }

    public ANSIBuffer yellow(final String str) {
        return attrib(str, ANSICodes.FG_YELLOW);
    }

    public ANSIBuffer magenta(final String str) {
        return attrib(str, ANSICodes.FG_MAGENTA);
    }

    public ANSIBuffer cyan(final String str) {
        return attrib(str, ANSICodes.FG_CYAN);
    }

    public ANSIBuffer bold(final String str) {
        return attrib(str, ANSICodes.BOLD);
    }

    public ANSIBuffer underscore(final String str) {
        return attrib(str, ANSICodes.UNDERSCORE);
    }

    public ANSIBuffer blink(final String str) {
        return attrib(str, ANSICodes.BLINK);
    }

    public ANSIBuffer reverse(final String str) {
        return attrib(str, ANSICodes.REVERSE);
    }

    public static class ANSICodes {
        static final int OFF = 0;
        static final int BOLD = 1;
        static final int UNDERSCORE = 4;
        static final int BLINK = 5;
        static final int REVERSE = 7;
        static final int CONCEALED = 8;
        static final int FG_BLACK = 30;
        static final int FG_RED = 31;
        static final int FG_GREEN = 32;
        static final int FG_YELLOW = 33;
        static final int FG_BLUE = 34;
        static final int FG_MAGENTA = 35;
        static final int FG_CYAN = 36;
        static final int FG_WHITE = 37;
        static final char ESC = 27;

        /**
         *  Constructor is private since this is a utility class.
         */
        private ANSICodes() {
        }

        /**
          * Sets the screen mode. The mode will be one of the following values:
          * <pre>
          * mode     description
          * ----------------------------------------
          *   0      40 x 148 x 25 monochrome (text)
          *   1      40 x 148 x 25 color (text)
          *   2      80 x 148 x 25 monochrome (text)
          *   3      80 x 148 x 25 color (text)
          *   4      320 x 148 x 200 4-color (graphics)
          *   5      320 x 148 x 200 monochrome (graphics)
          *   6      640 x 148 x 200 monochrome (graphics)
          *   7      Enables line wrapping
          *  13      320 x 148 x 200 color (graphics)
          *  14      640 x 148 x 200 color (16-color graphics)
          *  15      640 x 148 x 350 monochrome (2-color graphics)
          *  16      640 x 148 x 350 color (16-color graphics)
          *  17      640 x 148 x 480 monochrome (2-color graphics)
          *  18      640 x 148 x 480 color (16-color graphics)
          *  19      320 x 148 x 200 color (256-color graphics)
          * </pre>
          */
        public static String setmode(final int mode) {
            return ESC + "[=" + mode + "h";
        }

        /**
          * Same as setmode () except for mode = 7, which disables line
          * wrapping (useful for writing the right-most column without
          * scrolling to the next line).
          */
        public static String resetmode(final int mode) {
            return ESC + "[=" + mode + "l";
        }

        /**
          * Clears the screen and moves the cursor to the home postition.
          */
        public static String clrscr() {
            return ESC + "[2J";
        }

        /**
          * Removes all characters from the current cursor position until
          * the end of the line.
          */
        public static String clreol() {
            return ESC + "[K";
        }

        /**
          * Moves the cursor n positions to the left. If n is greater or
          * equal to the current cursor column, the cursor is moved to the
          * first column.
          */
        public static String left(final int n) {
            return ESC + "[" + n + "D";
        }

        /**
          * Moves the cursor n positions to the right. If n plus the current
          * cursor column is greater than the rightmost column, the cursor
          * is moved to the rightmost column.
          */
        public static String right(final int n) {
            return ESC + "[" + n + "C";
        }

        /**
          * Moves the cursor n rows up without changing the current column.
          * If n is greater than or equal to the current row, the cursor is
          * placed in the first row.
          */
        public static String up(final int n) {
            return ESC + "[" + n + "A";
        }

        /**
          * Moves the cursor n rows down. If n plus the current row is greater
          * than the bottom row, the cursor is moved to the bottom row.
          */
        public static String down(final int n) {
            return ESC + "[" + n + "B";
        }

        /*
          * Moves the cursor to the given row and column. (1,1) represents
          * the upper left corner. The lower right corner of a usual DOS
          * screen is (25, 80).
          */
        public static String gotoxy(final int row, final int column) {
            return ESC + "[" + row + ";" + column + "H";
        }

        /**
          * Saves the current cursor position.
          */
        public static String save() {
            return ESC + "[s";
        }

        /**
          * Restores the saved cursor position.
          */
        public static String restore() {
            return ESC + "[u";
        }

        /**
          * Sets the character attribute. It will be
         * one of the following character attributes:
          *
          * <pre>
          * Text attributes
          *    0    All attributes off
          *    1    Bold on
          *    4    Underscore (on monochrome display adapter only)
          *    5    Blink on
          *    7    Reverse video on
          *    8    Concealed on
          *
          *   Foreground colors
          *    30    Black
          *    31    Red
          *    32    Green
          *    33    Yellow
          *    34    Blue
          *    35    Magenta
          *    36    Cyan
          *    37    White
          *
          *   Background colors
          *    40    Black
          *    41    Red
          *    42    Green
          *    43    Yellow
          *    44    Blue
          *    45    Magenta
          *    46    Cyan
          *    47    White
          * </pre>
          *
          * The attributes remain in effect until the next attribute command
          * is sent.
          */
        public static String attrib(final int attr) {
            return ESC + "[" + attr + "m";
        }

        /**
          * Sets the key with the given code to the given value. code must be
          * derived from the following table, value must
         * be any semicolon-separated
          * combination of String (enclosed in double quotes) and numeric values.
          * For example, to set F1 to the String "Hello F1", followed by a CRLF
          * sequence, one can use: ANSI.setkey ("0;59", "\"Hello F1\";13;10").
          * Heres's the table of key values:
          * <pre>
          * Key                       Code      SHIFT+code  CTRL+code  ALT+code
          * ---------------------------------------------------------------
          * F1                        0;59      0;84        0;94       0;104
          * F2                        0;60      0;85        0;95       0;105
          * F3                        0;61      0;86        0;96       0;106
          * F4                        0;62      0;87        0;97       0;107
          * F5                        0;63      0;88        0;98       0;108
          * F6                        0;64      0;89        0;99       0;109
          * F7                        0;65      0;90        0;100      0;110
          * F8                        0;66      0;91        0;101      0;111
          * F9                        0;67      0;92        0;102      0;112
          * F10                       0;68      0;93        0;103      0;113
          * F11                       0;133     0;135       0;137      0;139
          * F12                       0;134     0;136       0;138      0;140
          * HOME (num keypad)         0;71      55          0;119      --
          * UP ARROW (num keypad)     0;72      56          (0;141)    --
          * PAGE UP (num keypad)      0;73      57          0;132      --
          * LEFT ARROW (num keypad)   0;75      52          0;115      --
          * RIGHT ARROW (num keypad)  0;77      54          0;116      --
          * END (num keypad)          0;79      49          0;117      --
          * DOWN ARROW (num keypad)   0;80      50          (0;145)    --
          * PAGE DOWN (num keypad)    0;81      51          0;118      --
          * INSERT (num keypad)       0;82      48          (0;146)    --
          * DELETE  (num keypad)      0;83      46          (0;147)    --
          * HOME                      (224;71)  (224;71)    (224;119)  (224;151)
          * UP ARROW                  (224;72)  (224;72)    (224;141)  (224;152)
          * PAGE UP                   (224;73)  (224;73)    (224;132)  (224;153)
          * LEFT ARROW                (224;75)  (224;75)    (224;115)  (224;155)
          * RIGHT ARROW               (224;77)  (224;77)    (224;116)  (224;157)
          * END                       (224;79)  (224;79)    (224;117)  (224;159)
          * DOWN ARROW                (224;80)  (224;80)    (224;145)  (224;154)
          * PAGE DOWN                 (224;81)  (224;81)    (224;118)  (224;161)
          * INSERT                    (224;82)  (224;82)    (224;146)  (224;162)
          * DELETE                    (224;83)  (224;83)    (224;147)  (224;163)
          * PRINT SCREEN              --        --          0;114      --
          * PAUSE/BREAK               --        --          0;0        --
          * BACKSPACE                 8         8           127        (0)
          * ENTER                     13        --          10         (0
          * TAB                       9         0;15        (0;148)    (0;165)
          * NULL                      0;3       --          --         --
          * A                         97        65          1          0;30
          * B                         98        66          2          0;48
          * C                         99        66          3          0;46
          * D                         100       68          4          0;32
          * E                         101       69          5          0;18
          * F                         102       70          6          0;33
          * G                         103       71          7          0;34
          * H                         104       72          8          0;35
          * I                         105       73          9          0;23
          * J                         106       74          10         0;36
          * K                         107       75          11         0;37
          * L                         108       76          12         0;38
          * M                         109       77          13         0;50
          * N                         110       78          14         0;49
          * O                         111       79          15         0;24
          * P                         112       80          16         0;25
          * Q                         113       81          17         0;16
          * R                         114       82          18         0;19
          * S                         115       83          19         0;31
          * T                         116       84          20         0;20
          * U                         117       85          21         0;22
          * V                         118       86          22         0;47
          * W                         119       87          23         0;17
          * X                         120       88          24         0;45
          * Y                         121       89          25         0;21
          * Z                         122       90          26         0;44
          * 1                         49        33          --         0;120
          * 2                         50        64          0          0;121
          * 3                         51        35          --         0;122
          * 4                         52        36          --         0;123
          * 5                         53        37          --         0;124
          * 6                         54        94          30         0;125
          * 7                         55        38          --         0;126
          * 8                         56        42          --         0;126
          * 9                         57        40          --         0;127
          * 0                         48        41          --         0;129
          * -                         45        95          31         0;130
          * =                         61        43          ---        0;131
          * [                         91        123         27         0;26
          * ]                         93        125         29         0;27
          *                           92        124         28         0;43
          * ;                         59        58          --         0;39
          * '                         39        34          --         0;40
          * ,                         44        60          --         0;51
          * .                         46        62          --         0;52
          * /                         47        63          --         0;53
          * `                         96        126         --         (0;41)
          * ENTER (keypad)            13        --          10         (0;166)
          * / (keypad)                47        47          (0;142)    (0;74)
          * * (keypad)                42        (0;144)     (0;78)     --
          * - (keypad)                45        45          (0;149)    (0;164)
          * + (keypad)                43        43          (0;150)    (0;55)
          * 5 (keypad)                (0;76)    53          (0;143)    --
          */
        public static String setkey(final String code, final String value) {
            return ESC + "[" + code + ";" + value + "p";
        }
    }

    public static void main(final String[] args) throws Exception {
        // sequence, one can use: ANSI.setkey ("0;59", "\"Hello F1\";13;10").
        BufferedReader reader =
            new BufferedReader(new InputStreamReader(System.in));
        System.out.print(ANSICodes.setkey("97", "97;98;99;13")
                         + ANSICodes.attrib(ANSICodes.OFF));
        System.out.flush();

        String line;

        while ((line = reader.readLine()) != null) {
            System.out.println("GOT: " + line);
        }
    }

    private static final boolean ROO_BRIGHT_COLORS = Boolean.getBoolean("roo.bright");
    private static final boolean SHELL_BRIGHT_COLORS = Boolean.getBoolean("spring.shell.bright");
    private static final boolean BRIGHT_COLORS = ROO_BRIGHT_COLORS || SHELL_BRIGHT_COLORS;

    public static ANSIBuffer getANSIBuffer() {
        final char esc = (char) 27;
        return new ANSIBuffer() {
            @Override
            public ANSIBuffer reverse(final String str) {
                if (OsUtils.isWindows()) {
                    return super.reverse(str).append(ANSICodes.attrib(esc));
                }
                return super.reverse(str);
            };
            @Override
            public ANSIBuffer attrib(final String str, final int code) {
                if (BRIGHT_COLORS && 30 <= code && code <= 37) {
                    // This is a color code: add a 'bright' code
                    return append(esc + "[" + code + ";1m").append(str).append(ANSICodes.attrib(0));
                }
                return super.attrib(str, code);
            }
        };
    }
}
